#include <stdbool.h>
#include "includes.h"


bool isActionCard(enum CARD c)
{
    // if (!(c >= curse && c <= treasure_map))
    // {
    //     g_debug("**** c = %i\n", c);
    // }
    // assert(c >= curse && c <= treasure_map);
    return cards[c].action;
}

bool isTreasureCard(enum CARD c)
{
    return cards[c].treasure;
}

bool isVictoryCard(enum CARD c)
{
    return cards[c].victory;
}



bool checkHandsAndDecks(struct gameState *gs)

{
    int nPlayers = gs->numPlayers;
    for (int p = 0; p < nPlayers; p++)
    {
        if (p == 0)
        {
            CU_ASSERT( gs->handCount[p] == 5)
        }
        else
        {
            CU_ASSERT(gs->handCount[p] == 0)
        }

        int ncopper = 0, nestate = 0;
        for (int i = 0; i < gs->deckCount[p]; i ++)
        {

            if (gs->deck[p][i] == copper)
            {
                ncopper++;
            }
            if (gs->deck[p][i] == estate)
            {
                nestate++;
            }
        }

        if (p == 0)
        {
            for (int i = 0; i < gs->handCount[0]; i++)
            {
                CU_ASSERT(gs->hand[0][i] == estate || gs->hand[0][i] == copper);
                if (gs->hand[0][i] == copper)
                {
                    ncopper++;
                }
                if (gs->hand[0][i] == estate)
                {
                    nestate++;
                }
            }
        }


        CU_ASSERT(ncopper == 7);
        CU_ASSERT(nestate == 3);
    }
    return true;
}


struct gameState *setup (int (*kc)[], int *randomSeed, int *nPlayers )
{
    static struct gameState  *gs;
    gs = newGame();

    if (kc == NULL)
    {
        int defaultkc[] = {gardens, mine, remodel, smithy, village, baron, great_hall, minion, steward, tribute};
        kc = &defaultkc;
    }

    if (randomSeed == NULL)
    {
        int defaultseed = 1;

        randomSeed = &defaultseed;
    }

    if (nPlayers == NULL)
    {
        int defaultplayers = 2;

        nPlayers = &defaultplayers;
    }


    initializeGame(*nPlayers, *kc, *randomSeed, gs);
    return gs;

}

void  setSupplyCount(enum CARD card, int n, struct gameState *gs)
{
    gs->supplyCount[card] = n;
}

bool isValidCard(enum CARD c)
{
    if ( (c < curse || c > treasure_map) && (c != -1))
    {
        printf("\nBad Card: %i\n", c);
        return false;
    }
    return true;
}

bool isValidState(struct gameState *gs)
{
    // returns true if gs is a valid gameState

    if (gs == NULL)
    {
        printf("\nNull gamestate\n");
        return false;
    }

    if (gs->numPlayers < 2 || gs->numPlayers > MAX_PLAYERS)
    {
        printf("invalid number of players: %i\n", gs->numPlayers);
        return false;
    }

    for (enum CARD c; c <= treasure_map; c++)
    {
        // ****** TODO add supply of each card to card_data, compare to actual supply pile counts
        // there are 60 copper cards, which is the largest supply of any card in the gameState
        // Use 60 as the maximum for all supply piles
        // supply count is -1 if card not used in game
        if (gs->supplyCount[c] < -1 || gs->supplyCount[c] > 60)
        {
            printf("Bad supply pile count: %s: %i\n", cards[c].name, gs->supplyCount[c]);
            return false;
        }
    }

    if (gs->whoseTurn < 0 || gs->whoseTurn > gs->numPlayers)
    {
        printf("Invalid whoseTurn: %i\n", gs->whoseTurn);
        return false;
    }

    for (int p = 0; p < gs->numPlayers; p++)
    {
        if (gs->handCount[p] < 0)
        {
            printf("handCount < 0\n");
            return false;
        }

        if (gs->deckCount[p] < 0)
        {
            printf("deckCount < 0\n");

            return false;
        }

        if (gs->discardCount[p] < 0)
        {
            printf("discardCount < 0\n");

            return false;
        }
    }

    for (int p = 0; p < gs->numPlayers; p++)
    {
        for (int i = 0; i < gs->deckCount[p]; i++)
        {
            if (!isValidCard(gs->deck[p][i]))
            {
                return false;
            }
        }
    }

    for (int p = 0; p < gs->numPlayers; p++)
    {
        for (int i = 0; i < gs->discardCount[p]; i++)
        {
            if (!isValidCard(gs->discard[p][i]))
            {
                return false;
            }
        }
    }

    for (int p = 0; p < gs->numPlayers; p++)
    {
        for (int i = 0; i < gs->handCount[p]; i++)
        {
            if (!isValidCard(gs->hand[p][i]))
            {
                return false;
            }
        }
    }

    // ******TODO complete this function

    return true;
}

void setHand(int player, int ncards, enum CARD cardsInHand[], struct gameState *gs)
{
    if (ncards > MAX_HAND)
    {
        fprintf(stderr, "setHand: Too many cards");
    }
    gs->handCount[player] = ncards;
    for (int pos = 0; pos < ncards; pos++)
    {
        gs->hand[player][pos] = cardsInHand[pos];
    }
}

void setDeck(int player, int ncards, enum CARD cardsInDeck[], struct gameState *gs)
{
    if (ncards > MAX_DECK)
    {
        fprintf(stderr, "setHand: Too many cards");
    }
    gs->deckCount[player] = ncards;
    for (int pos = 0; pos < ncards; pos++)
    {
        gs->deck[player][pos] = cardsInDeck[pos];
    }
}

void setDiscard(int player, int ncards, enum CARD cardsInDiscard[], struct gameState *gs)
{
    if (ncards > MAX_HAND)
    {
        fprintf(stderr, "setDiscard: Too many cards");
    }
    gs->discardCount[player] = ncards;
    for (int pos = 0; pos < ncards; pos++)
    {
        gs->discard[player][pos] = cardsInDiscard[pos];
    }
}

void showHand(int player, struct gameState *gs)
{
    printf("\nPlayer %i's Hand: ", player);
    for (int i = 0; i < gs->handCount[player]; i++)
    {
        printf("%s  %i  ", cards[gs->hand[player][i]].name, cards[gs->hand[player][i]].value);
    }
    printf("\n\n");
}

void showDeck(int player, struct gameState *gs)
{
    printf("\nPlayer %i's Deck: ", player);
    for (int i = 0; i < gs->deckCount[player]; i++)
    {
        printf("%s  %i ", cards[gs->deck[player][i]].name, cards[gs->deck[player][i]].value);
    }
    printf("\n\n");
}

void showDiscard(int player, struct gameState *gs)
{
    printf("\nPlayer %i's Discards: ", player);
    for (int i = 0; i < gs->discardCount[player]; i++)
    {
        printf("%s  %i  ", cards[gs->discard[player][i]].name, cards[gs->discard[player][i]].value);
    }
    printf("\n\n");
}

int getScore(int player, struct gameState *gs)
{
    int score = 0;
    int gardens_count = 0;
    enum CARD card;

    for (int i = 0; i < gs->handCount[player]; i++)
    {
        card = gs->hand[player][i];
        if (cards[card].victory)
        {
            score += cards[card].value;
            if (card == gardens)
            {
                gardens_count++;
            }
        }
    }

    for (int i = 0; i < gs->deckCount[player]; i++)
    {
        card = gs->deck[player][i];
        if (cards[card].victory)
        {
            score += cards[card].value;
            if (card == gardens)
            {
                gardens_count++;
            }
        }
    }

    for (int i = 0; i < gs->discardCount[player]; i++)
    {
        card = gs->discard[player][i];
        if (cards[card].victory)
        {

            score += cards[card].value;
            if (card == gardens)
            {
                gardens_count++;
            }
        }
    }

    int card_count = gs->handCount[player] + gs->deckCount[player] + gs->discardCount[player];
    score += gardens_count * (card_count / 10);

    return score;
}

void chooseCards(unsigned int k, enum CARD *chosenCards)
{
    // return k cards, not necessarily distinct
   
    for (int i = 0; i < k; ++i)
    {
        chosenCards[i] = randomRange(curse, treasure_map);
    }
}


void randCards(struct gameState *gs)
{
    // sets each players deck, hand, and discard to hold a random number of randomly chosen cards
    int nPlayers = gs->numPlayers;
    for (int player = 0; player < nPlayers; player++)
    {

        int nCardsInHand = randomRange(0, MAX_HAND);
        enum CARD *chosenCards = malloc(nCardsInHand * sizeof(enum CARD));
        chooseCards(nCardsInHand, chosenCards);
        gs->handCount[player] = nCardsInHand;
        for (int i = 0; i < nCardsInHand; i++)
        {
            gs->hand[player][i] = chosenCards[i];
        }
        free(chosenCards);


        int nCardsInDeck = randomRange(0, MAX_DECK);
        chosenCards = malloc(nCardsInDeck * sizeof(enum CARD));
        chooseCards(nCardsInDeck, chosenCards);
        gs->deckCount[player] = nCardsInDeck;
        for (int i = 0; i < nCardsInDeck; i++)
        {
            gs->deck[player][i] = chosenCards[i];
        }
        free(chosenCards);


        int nCardsInDiscard = randomRange(0, MAX_DECK);
        chosenCards = malloc(nCardsInDiscard * sizeof(enum CARD));
        chooseCards(nCardsInDiscard, chosenCards);
        gs->discardCount[player] = nCardsInDiscard;
        for (int i = 0; i < nCardsInDiscard; i++)
        {
            gs->discard[player][i] = chosenCards[i];
        }
        free(chosenCards);
    }

}

int randomRange(int a, int b)
{
    // Returns a random integer in the range [a, b]

    if (a < b)
    {
        // return gsl_rng_uniform_int(rng, (b - a + 1)) + a;
        return a + rand() % (b - a);
    }
    else
    {
        return 0;
    }
}
